--===========================================================================--
-- Naziv		: Execution
-- Ime fajla	: ex.vhdl  
-- Verzija		: 0.2
--===========================================================================-- 

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ex is 
	  port (
		  --IN
		    reset,clk,clk2  : in std_logic;
			PC_of           : in std_logic_vector (15 downto 0 );
			R1_of,R2_of,R3_of     : in std_logic_vector (3 downto 0 );
			Reg1,Reg2,Reg3  : in std_logic_vector (15 downto 0 ); --dolaze iz registarskog fajla
			Imm_of          : in std_logic_vector (7 downto 0 );
			opcode_of       : in std_logic_vector(4 downto 0);
			flush_ex  		: in std_logic;
			D_DBUS          : in std_logic_vector(15 downto 0);--Podatak koji dolazi iz DMem
			
		  --Buffer-pogledaj zasto
			opcode_ex       : buffer std_logic_vector(4 downto 0);
		  --OUT
			branch          : out std_logic;
			result          : out std_logic_vector (15 downto 0 );
			R1_ex,R2_ex,R3_ex     : out std_logic_vector (3 downto 0 );
			newPC           : out std_logic_vector (15 downto 0 );
			stackOverflow   : out std_logic	;
			D_DBUS_ex       : out std_logic_vector(15 downto 0)	
	  );
end;

architecture ex_AR of ex is
--TODO Proveri da li ovako treba stek
type stack_array is array( 255 downto 0 ) of std_logic_vector(15  downto 0);
signal stack: stack_array;
signal  sp: integer:=0;
--Stek raste od nizih ka visim adresama i pokazuje na prvu slobodnu
signal Reg1_ex,Reg2_ex ,Reg3_ex :  std_logic_vector (15 downto 0 );
signal Imm_ex                  :  std_logic_vector (7 downto 0 );
--Ovo je ubaceno zbog instrukcija POP i POPREL
signal resultStack :  std_logic_vector (15 downto 0 );
--Ovo je ubaceno zbog citanja iz memorije kod LOAD
signal D_DBUS_reg  :  std_logic_vector(15 downto 0);

begin
	ex_PR: process (clk,reset) is
	variable newSP : integer:=0;
	begin
		if reset='1' then
			opcode_ex<="00000";
			R1_ex<="0000";
			R2_ex<="0000";
			R3_ex<="0000";
			sp<=0;
			Imm_ex<="00000000";
			Reg1_ex<="0000000000000000";
			Reg2_ex <="0000000000000000";
			Reg3_ex<="0000000000000000";
			stackOverflow<='0';
		elsif rising_edge(clk)  then
			if flush_ex='1' then
				opcode_ex<="00000";
				R1_ex<="0000";
				R2_ex<="0000";
				R3_ex<="0000";
				Imm_ex<="00000000";
				Reg1_ex<="0000000000000000";
				Reg2_ex <="0000000000000000";
				Reg3_ex<="0000000000000000";
			else
				opcode_ex<=opcode_of;
				R1_ex<=R1_of;
				R2_ex<=R2_of;
				R3_ex<=R3_of;
				Reg1_ex<=Reg1;
				Reg2_ex<=Reg2;
				Reg3_ex<=Reg3;
				Imm_ex<=Imm_of;
				newSP:=sp;	
				if 	opcode_of="10101" then 		--JSR		    
					stack(newSP)<=std_logic_vector(unsigned(PC_of)+1 );--+1 zato sto smo cuvali poslednju koja se izvrsavala, a ne prvu narednu
					newSP:=newSP+1;
					if newSP>255 then 
						stackOverflow<='1';
					end if;
					resultStack<=Reg1;
					sp<=newSP;
				elsif opcode_of="10110" then 	--RTS 
					newSP:=newSP-1;
					if newSP<0 then 
						stackOverflow<='1';
					else 
						resultStack<=stack(newSP);--Ovo je vraceno na staro
					end if;				
					sp<=newSP;
				elsif opcode_of="10011" then 	--PUSH		    
					stack(newSP)<=Reg1;
					newSP:=newSP+1;
					if newSP>255 then 
						stackOverflow<='1';
					end if;
					sp<=newSP;			
				elsif opcode_of="10100" then 	--POP		    
					newSP:=newSP-1;
					if newSP<0 then 
						stackOverflow<='1';
					else 
						resultStack<=stack(newSP);
					end if;				
					sp<=newSP;
				elsif opcode_of="01000" then 	--POPREL
					newSP:=newSP-to_integer(signed(Imm_of));
					if newSP<0 or newSP>255  then 
						stackOverflow<='1';
					else 
						resultStack<=stack(newSP);
					end if;				
				end if;
			end if;
		end if;
	end process; 
	
	 --Ovde ide kod za citanje operanda iz DMem
	 ex_DMem_PR: process (clk,reset) is
		--???Cemu ovde sluzi count??? 
		--Meni se cini da moze i bez njega, verovatno ima neke veze sa kasnjenjem clk2 od Pi/2
		--TODO Ajde Srecko pogledaj ovo, pa ako se slazes i ti da ne treba da izbacimo glupi count
		--variable count: integer:=0;
		begin
			if reset='1' then
				--count:=0;
				D_DBUS_reg<="0000000000000000";
			elsif falling_edge(clk) then
				--count:=count+1;
				--if count=2 then 
					D_DBUS_reg<=D_DBUS;				
					--count:=0;
				--end if;
			end if;			
		end process;
	  --TODO Prakticno ovde dobijamo podatak iz DMem i prosledjujemo ga u WB,
	  -- 	 jer svi upisi u registre treba da idu iz WB
	  D_DBUS_ex<= D_DBUS_reg;

	 --skokovi   
	 branch <=  '1' when ((opcode_ex="01001" and Reg1_ex >  Reg2_ex)
					 or   (opcode_ex="01010" and Reg1_ex >= Reg2_ex)
					 or   (opcode_ex="01011" and Reg1_ex <  Reg2_ex)
					 or   (opcode_ex="01100" and Reg1_ex <= Reg2_ex)
					 or   (opcode_ex="01101" and Reg1_ex  = Reg2_ex)
					 or   (opcode_ex="01110" and Reg1_ex /= Reg2_ex)
					 or    opcode_ex="10101" or opcode_ex="10110"    --JSR i RTS
					 )else  
				'0';
	 newPC<=Reg3_ex when ((opcode_ex="01001" and Reg1_ex >  Reg2_ex)  --BG
					 or   (opcode_ex="01010" and Reg1_ex >= Reg2_ex)  --BGE
					 or   (opcode_ex="01011" and Reg1_ex <  Reg2_ex)  --BL
					 or   (opcode_ex="01100" and Reg1_ex <= Reg2_ex)  --BLE
					 or   (opcode_ex="01101" and Reg1_ex  = Reg2_ex)  --BE
					 or   (opcode_ex="01110" and Reg1_ex /= Reg2_ex)  --BNE
					 )else  
			resultStack when (opcode_ex="10101" or opcode_ex="10110") --JSR i RTS
					else
				"0000000000000000";
	 
	 --ALU		
	 result <= Reg1_ex when opcode_ex="00001" else --MOV
			   std_logic_vector(unsigned(Reg1_ex)  ror to_integer(unsigned(Imm_ex))) when opcode_ex="00010" else --ROR 
			   std_logic_vector(unsigned(Reg1_ex) + unsigned(Reg2_ex)) when opcode_ex="00011" else  --ADD
			   std_logic_vector(unsigned(Reg1_ex) - unsigned(Reg2_ex)) when opcode_ex="00100" else  --SUB
			   Reg1_ex and Reg2_ex when opcode_ex="00101" else
			   Reg1_ex or Reg2_ex when opcode_ex="00110" else
			   Reg1_ex xor Reg2_ex when opcode_ex="00111" else
			   Reg3_ex when ((opcode_ex="01001" and Reg1_ex >  Reg2_ex)
					 or   (opcode_ex="01010" and Reg1_ex >= Reg2_ex)
					 or   (opcode_ex="01011" and Reg1_ex <  Reg2_ex)
					 or   (opcode_ex="01100" and Reg1_ex <= Reg2_ex)
					 or   (opcode_ex="01101" and Reg1_ex  = Reg2_ex)
					 or   (opcode_ex="01110" and Reg1_ex /= Reg2_ex)
					 ) else --SKOKOVI
			   resultStack when opcode_ex="01000" else --POPREL
			   resultStack when  opcode_ex="10100"  else --POP			 
			   not Reg1_ex when opcode_ex="10111" else --NOT		
			   "0000000000000000";
end ex_AR;